//
//  PMCollision.m
//  PezMonkey
//
//  Created by Spence DiNicolantonio on 11/9/09.
//  Copyright 2009 Necrosoft. All rights reserved.
//

/**
 * A PMCollision object store's information about a collision between two PMShape
 * objects. Instances of PMCollision should NEVER be instantiated. Instantiation 
 * of PMCollision objects is performed internally for use in collision callbacks
 * and collision queries.
 * 
 * PMCollision objects should never be cached or retained, as their data is 
 * extremely volatile. The data stored in a PMCollision object will only last 
 * until the end of spacial step in which it was created.
 */

#import "PMCollision.h"
#import "PMSpace.h"
#import "PMShape.h"


#pragma mark -
#pragma mark Private Interface
@interface PMCollision (private)
+ (PMContact)contactWithcpContact:(cpContact)theContact;
@end
#pragma mark -


@implementation PMCollision

@synthesize space;
@synthesize shapeA;
@synthesize shapeB;
@synthesize contactCount;
@synthesize contacts;
@synthesize state;


#pragma mark -
#pragma mark Init

/**
 * Returns a PMCollision object for a given space initialized by copying the data 
 * of a given cpArbiter structure. Initialization of PMCollision objects is strictly 
 * for internal use.
 */
- (id)initWithSpace:(PMSpace *)theSpace arbiter:(cpArbiter *)arbiter state:(PMCollisionState)collisionState {
	if (![super init])
		return nil;
	
	// verify arbiter's shape's data pointers are PMShape references
	if (![(id)arbiter->a->data isKindOfClass:PMShape.class] || 
		![(id)arbiter->b->data isKindOfClass:PMShape.class]) {
		[NSException raise:@"Invalid cpShape data pointer" 
					format:@"cpShape data pointer not a valid PMShape object (cpShape created explicitly?)"];
		return nil;
	}
	
	// get data pointers from cpShapes
	shapeA = [(PMShape *)arbiter->a->data retain];
	shapeB = [(PMShape *)arbiter->b->data retain];
	
	// set space reference
	space = [theSpace retain];
	
	// set contact info
	contactCount = arbiter->numContacts;
	contacts = (PMContact *) malloc(contactCount * sizeof(PMContact));
	for (int i=0; i<contactCount; i++) {
		contacts[i] = [PMCollision contactWithcpContact:arbiter->contacts[i]];
	}
	
	// set collision state
	state = collisionState;
	
	return self;
}


#pragma mark Convenience

/**
 * Creates and returns a PMCollision object for a given space initialized by 
 * copying the data of a given cpArbiter structure. Creation of PMCollision objects
 * is strictly for internal use.
 */
+ (PMCollision *)collisionWithSpace:(PMSpace *)theSpace arbiter:(cpArbiter *)arbiter state:(PMCollisionState)collisionState {
	return [[[PMCollision alloc] initWithSpace:theSpace 
									   arbiter:arbiter
										 state:collisionState] autorelease];
}


#pragma mark Private

/**
 * Creates and returns a PMContact structure from a given cpContact and corresponding
 * normal coefficient.
 */
+ (PMContact)contactWithcpContact:(cpContact)theContact {
	PMContact newContact;
	newContact.point = theContact.p;
	newContact.normal = theContact.n;
	newContact.distance = theContact.dist;
	return newContact;
}


#pragma mark -
#pragma mark Dealloc

/**
 * Releases member objects before deallocating.
 */
- (void)dealloc {
	[space release];
	[shapeA release];
	[shapeB release];
	free(contacts);
	[super dealloc];
}


#pragma mark -
#pragma mark Getters

/**
 * Returns the contact at a given index in the receiver. Raises an exception if 
 * the given given index is out of bounds.
 */
- (PMContact)contact:(NSUInteger)contactIndex {
	// valid index?
	if (contactIndex >= contactCount) {
		[NSException raise:@"Index out of bounds" 
					format:@"Index %d is out of range [0,%d]", contactIndex, contactCount];
	}
	return contacts[contactIndex];
}
	
/**
 * Returns the position of the contact point at a given index in the receiver. 
 * Raises an exception if the given index is out of bounds.
 */
- (PMVect)positionOfContact:(NSUInteger)contactIndex {
	// valid index?
	if (contactIndex >= contactCount) {
		[NSException raise:@"Index out of bounds" 
					format:@"Index %d is out of range [0,%d]", contactIndex, contactCount];
	}
	return contacts[contactIndex].point;
}

/**
 * Returns the normal vector of the contact at a given index in the receiver. 
 * Raises an exception if the given index is out of bounds.
 */
- (PMVect)normalOfContact:(NSUInteger)contactIndex {
	// valid index?
	if (contactIndex >= contactCount) {
		[NSException raise:@"Index out of bounds" 
					format:@"Index %d is out of range [0,%d]", contactIndex, contactCount];
	}
	return contacts[contactIndex].normal;
}

/**
 * Returns the overlap distance of the contact at a given index in the receiver. 
 * Raises an exception if the given index is out of bounds.
 */
- (PMFloat)distanceOfContact:(NSUInteger)contactIndex {
	// valid index?
	if (contactIndex >= contactCount) {
		[NSException raise:@"Index out of bounds" 
					format:@"Index %d is out of range [0,%d]", contactIndex, contactCount];
	}
	return contacts[contactIndex].distance;
}

@end
